package com.ruoyi.framework.websocket;

import java.util.concurrent.Semaphore;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.core.domain.AjaxResult;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;

/**
 * websocket 消息处理
 * 
 * @author ruoyi
 */
@Component
@ServerEndpoint("/websocket/message")
public class WebSocketServer
{
    /**
     * WebSocketServer 日志控制器
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketServer.class);

    /**
     * 默认最多允许同时在线人数100
     */
    public static int socketMaxOnlineCount = 100;

    private static Semaphore socketSemaphore = new Semaphore(socketMaxOnlineCount);

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session) throws Exception
    {
        boolean semaphoreFlag = false;
        // 尝试获取信号量
        semaphoreFlag = SemaphoreUtils.tryAcquire(socketSemaphore);
        if (!semaphoreFlag)
        {
            // 未获取到信号量
            LOGGER.error("\n 当前在线人数超过限制数- {}", socketMaxOnlineCount);
            WebSocketUsers.sendMessageToUserByText(session, "当前在线人数超过限制数：" + socketMaxOnlineCount);
            session.close();
        }
        else
        {
            try {
                // 从URL参数中获取userId和userName
                String queryString = session.getQueryString();
                Map<String, String> params = parseQueryString(queryString);
                
                String userIdStr = params.get("userId");
                String userName = params.get("userName");
                
                if (StringUtils.isEmpty(userIdStr) || StringUtils.isEmpty(userName)) {
                    WebSocketUsers.sendMessageToUserByText(session, "连接参数错误：需要提供userId和userName");
                    session.close();
                    return;
                }
                
                Long userId = Long.parseLong(userIdStr);
                String sessionId = session.getId();
                
                // 添加用户
                WebSocketUsers.put(sessionId, session, userId, userName);
                LOGGER.info("\n 建立连接 - 用户ID: {}, 用户名: {}, 会话ID: {}", userId, userName, sessionId);
                LOGGER.info("\n 当前人数 - {}", WebSocketUsers.getUsers().size());
                
                // 发送连接成功消息
                JSONObject connectMessage = new JSONObject()
                    .fluentPut("type", "connect")
                    .fluentPut("sessionId", sessionId)
                    .fluentPut("userId", userId)
                    .fluentPut("userName", userName)
                    .fluentPut("message", "连接成功");
                WebSocketUsers.sendMessageToUserByText(session, JSON.toJSONString(connectMessage));
                
                // 广播最新的用户列表
                broadcastUserList();
            } catch (Exception e) {
                LOGGER.error("\n 处理连接参数时发生错误", e);
                WebSocketUsers.sendMessageToUserByText(session, "连接参数错误：" + e.getMessage());
                session.close();
                SemaphoreUtils.release(socketSemaphore);
            }
        }
    }

    /**
     * 解析WebSocket URL查询字符串
     */
    private Map<String, String> parseQueryString(String queryString) {
        Map<String, String> params = new HashMap<>();
        if (StringUtils.isNotEmpty(queryString)) {
            String[] pairs = queryString.split("&");
            for (String pair : pairs) {
                String[] keyValue = pair.split("=");
                if (keyValue.length == 2) {
                    try {
                        String key = keyValue[0];
                        String value = URLDecoder.decode(keyValue[1], "UTF-8");
                        params.put(key, value);
                    } catch (UnsupportedEncodingException e) {
                        LOGGER.error("解析查询参数失败", e);
                    }
                }
            }
        }
        return params;
    }

    /**
     * 连接关闭时处理
     */
    @OnClose
    public void onClose(Session session)
    {
        LOGGER.info("\n 关闭连接 - {}", session);
        // 移除用户
        boolean removeFlag = WebSocketUsers.remove(session.getId());
        if (!removeFlag)
        {
            // 获取到信号量则需释放
            SemaphoreUtils.release(socketSemaphore);
        }
        // 广播最新的用户列表
        broadcastUserList();
    }

    /**
     * 广播在线用户列表
     */
    private void broadcastUserList()
    {
        try
        {
            // 构建用户列表
            JSONObject userListMessage = new JSONObject();
            userListMessage.put("type", "userList");
            
            List<JSONObject> userList = new ArrayList<>();
            for (Map.Entry<String, WebSocketUser> entry : WebSocketUsers.getUsers().entrySet()) {
                WebSocketUser user = entry.getValue();
                JSONObject userJson = new JSONObject()
                    .fluentPut("id", entry.getKey())
                    .fluentPut("userId", user.getUserId())
                    .fluentPut("userName", user.getUserName());
                userList.add(userJson);
            }
            
            userListMessage.put("users", userList);
            
            // 广播给所有用户
            WebSocketUsers.sendMessageToUsersByText(userListMessage.toJSONString());
        }
        catch (Exception e)
        {
            LOGGER.error("广播用户列表时发生错误", e);
        }
    }

    /**
     * 抛出异常时处理
     */
    @OnError
    public void onError(Session session, Throwable exception) throws Exception
    {
        if (session.isOpen())
        {
            // 关闭连接
            session.close();
        }
        String sessionId = session.getId();
        LOGGER.info("\n 连接异常 - {}", sessionId);
        LOGGER.info("\n 异常信息 - {}", exception);
        // 移出用户
        WebSocketUsers.remove(sessionId);
        // 获取到信号量则需释放
        SemaphoreUtils.release(socketSemaphore);
    }

    /**
     * 服务器接收到客户端消息时调用的方法
     */
    @OnMessage
    public void onMessage(String message, Session session)
    {
        try
        {
            JSONObject jsonObject = JSON.parseObject(message);
            String type = jsonObject.getString("type");
            String msg = jsonObject.getString("message");

            if (StringUtils.isEmpty(type) || StringUtils.isEmpty(msg))
            {
                WebSocketUsers.sendMessageToUserByText(session, "消息格式错误：type和message字段不能为空");
                return;
            }

            // 获取发送消息的用户信息
            String sessionId = session.getId();
            WebSocketUser sender = WebSocketUsers.get(sessionId);
            if (sender == null) {
                WebSocketUsers.sendMessageToUserByText(session, "用户会话无效");
                return;
            }

            // 构建包含发送者信息的消息
            JSONObject messageWithSender = new JSONObject()
                .fluentPut("type", type)
                .fluentPut("message", msg)
                .fluentPut("from", new JSONObject()
                    .fluentPut("sessionId", sessionId)
                    .fluentPut("userId", sender.getUserId())
                    .fluentPut("userName", sender.getUserName())
                )
                .fluentPut("time", System.currentTimeMillis());

            switch (type)
            {
                case "all":
                    // 发送给所有用户
                    WebSocketUsers.sendMessageToUsersByText(messageWithSender.toJSONString());
                    
                    // 发送确认消息
                    JSONObject confirmMsg = new JSONObject()
                        .fluentPut("type", "confirm")
                        .fluentPut("message", "群发消息成功");
                    WebSocketUsers.sendMessageToUserByText(session, confirmMsg.toJSONString());
                    break;
                case "user":
                    // 发送给指定用户
                    String toSessionId = jsonObject.getString("to");
                    if (StringUtils.isEmpty(toSessionId))
                    {
                        WebSocketUsers.sendMessageToUserByText(session, "消息格式错误：私聊消息必须指定to字段");
                        return;
                    }
                    
                    WebSocketUser recipient = WebSocketUsers.get(toSessionId);
                    if (recipient == null)
                    {
                        WebSocketUsers.sendMessageToUserByText(session, "用户 " + toSessionId + " 不在线");
                        return;
                    }
                    
                    // 添加接收者信息
                    messageWithSender.put("to", new JSONObject()
                        .fluentPut("sessionId", toSessionId)
                        .fluentPut("userId", recipient.getUserId())
                        .fluentPut("userName", recipient.getUserName())
                    );
                    
                    // 发送私聊消息
                    WebSocketUsers.sendMessageToUserByText(recipient.getSession(), messageWithSender.toJSONString());
                    
                    // 给发送者也发一份，便于客户端显示
                    WebSocketUsers.sendMessageToUserByText(session, messageWithSender.toJSONString());
                    
                    // 发送确认消息
                    JSONObject privateConfirmMsg = new JSONObject()
                        .fluentPut("type", "confirm")
                        .fluentPut("message", "私聊消息发送成功");
                    WebSocketUsers.sendMessageToUserByText(session, privateConfirmMsg.toJSONString());
                    break;
                case "getUsers":
                    // 获取在线用户列表
                    broadcastUserList();
                    break;
                default:
                    WebSocketUsers.sendMessageToUserByText(session, "不支持的消息类型：" + type);
            }
        }
        catch (Exception e)
        {
            LOGGER.error("处理消息时发生错误", e);
            WebSocketUsers.sendMessageToUserByText(session, "消息格式错误：" + e.getMessage());
        }
    }
}
